package com.example.gateway.predicate;

import jakarta.validation.constraints.NotEmpty;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;


/**
 * 自定义的路由断言工厂，用于判断请求中的参数是否符合我们自定义的条件，比如当前用户是不是vip，符合条件则路由到指定的微服务，不符合条件则路由到其他微服务
     这个类的名字要与最终使用时的断言配置项的名字一致
 如：        -Vip = user,wangjj
              表示匹配这个断言规则（Vip） ，且可以接收两个参数param和value，一个参数值为user，一个参数值wangjj
 下面要写检验逻辑，获取这个个属性的值，判断如果满足条件则通过
 */

@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {

    //固定格式呗，传入上面定义的config，接收那两个参数    param和value
    public VipRoutePredicateFactory() {
        super(Config.class);
    }

    /**
     *   //必须实现的方法，表示在使用哦个断言时，参数的顺序，比如-Vip = user,wangjj，这个参数的顺序就是param和value
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param", "value");
    }

    /**核心方法，这个方法是这个自定义断言的处理逻辑，方法返回ture，表示断言成功，取执行路由或者其他操作，返回false，表示断言失败
     * //必须实现的方法，用于获取配置文件使用断言时传入的两个参数值，执行自定义的条件判断
     * 首先明确：断言-Vip = user,wangjj的作用是：判断前端的请求localhost..？user=wangjj..是否携带HTTP请求参数user,且请求参数值为wangjj
     * 配置问及暗中设置后，这里的param属性配赋值为user，value属性赋值为wangjj
     * 然后取出请求参数中的user参数的值（？user=wangjj），与配置文件中传入的value属性(-Vip user,wangjj)值进行比较，如果相等则返回true，满足我的断言要求，否则返回false
     */
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                // config.param = "user" (来自使用断言时传入的的第一个参数)
                // config.value = "wangjj" (来自使用断言时的第二个参数)

                // 获取http的localhost轻轻中的请求参数
                ServerHttpRequest request = serverWebExchange.getRequest();

                // 获取请求参数中的 user 参数值（即请求示例：http://网关地址/路径?user=xxx）
                //这里用config.param实际就是"user"，就是取请求参数中找哪个请求参数名叫做config.param（即user）的请求参数的值，即xxx
                //http://网关地址/路径?user=xxx这样能拿到xxx ，，如果http://网关地址/路径?username=xxx，那就拿不到了
                //这个first就是请求参数中请求参数名为user的请求参数值，即xxx
                String first = request.getQueryParams().getFirst(config.param);

                //如果这个请求参数的值等于，写断言时传入的第二个参数值config.value，，这就实现了由配置问及暗中写的断言表达式来控制我的判断逻辑
                return StringUtils.hasText(first) && first.equals(config.value);
            }
        };
    }


    @Validated
    public static class Config {
        /**
         * 自定义这个断言规则可以使用怎样的属性名    -Vip = user,wangjj   --》即-Vid = param=user;value=wangjj
         */
        @NotEmpty
        private String param;
        @NotEmpty
        private String value;

        public @NotEmpty String getParam() {
            return param;
        }
        public void setParam(@NotEmpty String param) {
            this.param = param;
        }
        public @NotEmpty String getValue() {
            return value;
        }
        public void setValue(@NotEmpty String value) {
            this.value = value;
        }
    }

}
